package simpleftp.server;

import java.io.*;
import java.net.*;
import java.util.*;
import java.lang.Void;
import java.util.concurrent.Callable;

import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeModel;

import simpleftp.client.control.Command;

/**
 * ServeurFTP is a simple package that implements a Java FTP server. With
 * ServeurFTP, you can connect to an FTP client and download multiple files. 
 */
public class FTPServer implements Callable<Void> 
{
	private static boolean DEBUG = false;
	private String response, user;
	private int validations;
	private File f;
	private StringBuilder sb;
	private int portnum1, portnum2;
	private Properties p;
	private ServerSocket dl;
	private Socket dl2;
	private FileInputStream fis;
	private Socket socket = null;
	private BufferedReader reader = null;
	private BufferedWriter writer = null;
	private ObjectOutputStream objWriter=null;
	
	public FTPServer(Socket s) {
		socket = s;
		validations = 0;
		try {
			p = new Properties();
			fis = new FileInputStream("users.properties");
			p.load(fis);
			try {
				
				/*Bufferedreader: flux de caractère à lire
				 * Bufferedwriter: flux de caractère à écrire
				 * InputStreamReader: flux octet lire
				 * OutputStreamWriter: flux octet ecrire
				 *  */
				reader = new BufferedReader(new InputStreamReader(
						socket.getInputStream()));
				
				OutputStream out=socket.getOutputStream();
				writer = new BufferedWriter(new OutputStreamWriter(out));
				objWriter=new ObjectOutputStream(out);
				
				sendLine("220 ");
				validations++;
				f = new File(System.getProperty("user.home"));	
				System.out.println("FTP Server : new client from " + socket.getInetAddress().getHostAddress());
			} catch (IOException e) {
				try {
					sendLine("error");
				} catch (IOException e1) {
					e1.printStackTrace();
				}
				e.printStackTrace();
			}
		} catch (IOException ioex) {
			System.out.println("Error: Properties File didn't load");
			try {
				sendLine("error ");
			} catch (IOException e) {
				e.printStackTrace();
			}
			ioex.printStackTrace();
		}
	}

	
	public Void call() {
		try {
			response = readLine();
			if (response.startsWith("USER")) {
				if (p.containsKey(response.substring(5))) {
					user = response.substring(5);
					System.out.println("Server knows user " + response);
					sendLine("331 ");
					validations++;
				} else {
					System.out.println("Server does no know user " + response);
					sendLine("error ");
				}
			}
			response = readLine();
			if (response.startsWith("PASS")) {
				if (p.get((String) user).equals(response.substring(5))) {
					System.out.println("Server validates pass " + response);
					sendLine("230 ");
					validations++;
				} else {
					sendLine("error ");
				}
			}

		} catch (IOException e) {
			// TODO Auto-generated catch block
			try {
				sendLine("error ");
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			e.printStackTrace();
		}

		while (validations == 3) {
			try {
				response = readLine();

				switch (Command.valueOf(response.split(" ")[0])) {

				case PWD:
					sendLine("257 " + f.getAbsolutePath());
					break;

				case LIST:
					sb = new StringBuilder("200 ");
					sendLine(sb.toString());
					sendLine(getTreeModel());
					break;

				case CWD:
					String directory=response.split(" ")[1];
					System.out.println("CWD");
					System.out.println("CWD new current directory:"
							+ directory);
					f = new File(directory);
					if(f.isDirectory()){
					sendLine("250 ");}
					else sendLine("not directory");
					break;

				case PASV:
					pasv();
					break;

				case STOR:
					stor();
					break;
				
				case RETR:
					retr();
					break;	
					
				case QUIT:
					socket.close();
					System.out.println("QUIT :Goodbye");
					validations = 0;
					break;

				default:
					System.out.println("Unknown Command.");
					sendLine("error");
					break;

				}
			} catch (IOException e) {
				try {
					sendLine("error");
				} catch (IOException e1) {

					e1.printStackTrace();
				}
				e.printStackTrace();
			}

		}
		Void Void = null;
		return Void;
	}
	
	/**
	 *   SEND SERIALIZABLE OBJECT OVER THE NETWORK  
	 */
	private void sendLine(TreeModel treeModel) throws IOException {
		if (socket == null) {
			throw new IOException("FTPServer is not connected.");
		}
		try {
			objWriter.writeObject(treeModel);
			objWriter.flush();
			if (DEBUG) {
				System.out.println("> " + treeModel);
			}
		} catch (IOException e) {
			socket = null;
			throw e;
		}
	}
	/**
     * Sends a raw command to the FTP server.
     */

	private void sendLine(String line) throws IOException {
		if (socket == null) {
			throw new IOException("FTPServer is not connected.");
		}
		try {
			writer.write(line + "\r\n");
			writer.flush();
			if (DEBUG) {
				System.out.println("> " + line);
			}
		} catch (IOException e) {
			socket = null;
			throw e;
		}
	}

	
	
	private String readLine() throws IOException {
		String line = reader.readLine();
		if (DEBUG) {
			System.out.println("< " + line);
		}
		return line;
	}

	private void retr(){
		try {
			dl2 = dl.accept();
			sendLine("200 ");//creation fichier pour upload
			String path=readLine();
			if(path.split(" ")[0].equals("path")){
				path=path.split(" ")[1];
			}
			/*ecriture du fichier dans le flux de la socket*/
			BufferedInputStream input = new BufferedInputStream(new FileInputStream(new File(path)));
			BufferedOutputStream output = new BufferedOutputStream(dl2.getOutputStream());
			byte[] buffer = new byte[4096];//contient les octets du fichier upload
	        int bytesRead = 0;
	        while ((bytesRead = input.read(buffer)) != -1) {
	            output.write(buffer, 0, bytesRead);
	        }
	        output.flush();
	        output.close();
	        input.close();
	        dl.close();
			dl2.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	private void stor() {
		try {
			dl2 = dl.accept();
			sendLine("150 ");//creation fichier pour upload
			OutputStream os = new FileOutputStream(new File(response.substring(5)));
			InputStream is = dl2.getInputStream();
			byte[] buffer = new byte[4096];
			int bytesRead = 0;
			while ((bytesRead = is.read(buffer)) != -1) {
				os.write(buffer, 0, bytesRead);
			}
			os.flush();
			os.close();
			is.close();
			sendLine("226 ");
			dl.close();
			dl2.close();
			System.out.println("STOR " + response.split(" ")[1]
					+ " : File received");
		} catch (IOException e) {
			try {
				sendLine("error");
			} catch (IOException e1) {
				e1.printStackTrace();
			}
			e.printStackTrace();
		}
	}

	private void pasv() {
		try {
			dl = new ServerSocket(0);
			portnum1 = dl.getLocalPort() / 256;
			portnum2 = dl.getLocalPort() % 256;
			sendLine("227 ("
					+ InetAddress.getLocalHost().getHostAddress()
							.replace('.', ',') + "," + portnum1 + ","
					+ portnum2 + ")");
			System.out.println("PASV success");
		} catch (IOException e) {
			try {
				sendLine("error ");
				System.out.println("pasv error");
			} catch (IOException e1) {
				e1.printStackTrace();
			}
			e.printStackTrace();
		}
	}
	
	public static void setDebug(boolean b) {
		DEBUG = b;
	}
	
	
	private TreeModel getTreeModel()
	{
		DefaultMutableTreeNode userRoot = new DefaultMutableTreeNode(f.getAbsolutePath());
		try {
			populateTree(userRoot, f);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return new DefaultTreeModel(userRoot);
	}
	private void populateTree(DefaultMutableTreeNode userRoot, File root) throws IOException {
		String[] childNodes=root.list();int i=0;
		if(childNodes!=null){
			if(root==f)
			{
				sendLine(""+(childNodes.length));
			}
			for (i=0;i<childNodes.length;i++) {
				File childFile=new File(root, childNodes[i]);
				DefaultMutableTreeNode childNode =new DefaultMutableTreeNode(childFile.getAbsolutePath());
				if(childFile.list()!=null)
					populateTree(childNode, childFile);
				userRoot.add(childNode);
				if(root==f)
				{
					sendLine(""+i);
				}
			}
		}
		if(root==f)
		{
			sendLine("QUIT");
		}
	}
	
}
